﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Media;
using System.Net;
using System.Net.Cache;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shell;
using System.Windows.Threading;
using Microsoft.Win32;
using RealTimeStocks.Properties;

namespace RealTimeStocks
{
	public partial class MainWindow : Window
	{
		private WindowState PreviousWinState;
		private DispatcherTimer RefreshWBtimer = new DispatcherTimer();
		private DispatcherTimer ProgressTimer = new DispatcherTimer();
		private DispatcherTimer GetValuesTimer = new DispatcherTimer();
		private string StocksForCheck { get; set; }
		private IList<StockParameters> _myStocksParams = new List<StockParameters>();
		internal IList<StockParameters> MyStocksParams
		{
			get { return _myStocksParams.OrderBy(e => e.IsSumRow).ThenBy(e => e.StockName).ToList(); }
			set
			{
				_myStocksParams = value;
				StockParameters obj = new StockParameters("-Sum-");
				obj.IsSumRow = true;
				_myStocksParams.Add(obj);
				dgMyStocks.ItemsSource = MyStocksParams;
			}
		}

		public int GetValuesInterval
		{
			get
			{
				int n = 0;
				if (int.TryParse(txtGetValuesSecs.Text, out n))
					return n;
				return 3;
			}
		}

		public decimal AmountGained
		{
			get
			{
				decimal tmp = 0;
				decimal.TryParse(txtAlreadyGained.Text, out tmp);
				return tmp;
			}
		}

		public decimal AmountLost
		{
			get
			{
				decimal tmp = 0;
				decimal.TryParse(txtAlreadyLost.Text, out tmp);
				return tmp;
			}
		}

		private int _ErrorCount = 0;
		public int ErrorCount
		{
			get { return _ErrorCount; }
			set
			{
				if (value % 10 == 5)
					WakeUp();
				_ErrorCount = value;
			}
		}

		private int REFRESHINTERVAL = 60;
		public MainWindow()
		{
			InitializeComponent();
			RefreshWBtimer.Tick += new EventHandler(refreshWBtimer_Tick);
			RefreshWBtimer.Interval = new TimeSpan(0, 0, REFRESHINTERVAL);
			RefreshWBtimer.Start();
			GetValuesTimer.Tick += new EventHandler(getValuesTimer_Tick);
			GetValuesTimer.Interval = new TimeSpan(0, 0, GetValuesInterval);
			ProgressTimer.Tick += new EventHandler(ProgressTimer_Tick);
			ProgressTimer.Interval = new TimeSpan(0, 0, 2);
			ProgressTimer.Start();
		}

		void ProgressTimer_Tick(object sender, EventArgs e)
		{
			TaskbarItemInfo.ProgressValue += (double)2 / (REFRESHINTERVAL + GetValuesInterval);
		}

		private void refreshWBtimer_Tick(object sender, EventArgs e)
		{
			GetValues();
		}

		private void GetValues()
		{
			if (rbNaft.IsChecked.Value)
				wb.Navigate("http://www.naftemporiki.gr/finance/athexRealTimeTicker");
			else if (rbCapt.IsChecked.Value)
				wb.Navigate("http://www.capital.gr/ticker/RealtimeHtmlTicker.aspx");
		}

		private void getValuesTimer_Tick(object sender, EventArgs e)
		{
			GetValuesTimer.Stop();
			CheckStockValues();
			TaskbarItemInfo.ProgressValue = 0;
		}

		private void CheckStockValues()
		{
			if (rbNaft.IsChecked.Value)
				CheckStockValuesNaft();
			else if (rbCapt.IsChecked.Value)
				CheckStockValuesCapt();
		}

		private IList<StockValuePrc> LatestStocksAndValues;
		//private IList<StockValuePrc> HistoryStocksAndValues = new List<StockValuePrc>();
		private void CheckStockValuesNaft()
		{
			txtMessage.Text = "";
			Mouse.OverrideCursor = Cursors.Wait;
			LatestStocksAndValues = new List<StockValuePrc>();
			try
			{
				StocksForCheck = string.Join("|", MyStocksParams.Where(e => !e.IsSumRow).Select(e => e.ToString()));
				dynamic doc = wb.Document;
				string htmlText = doc.documentElement.InnerHtml;
				int index = htmlText.IndexOf("tickerContainer", 0);
				if (index == -1)
				{
					WriteToLogFile("Invalid html response. No 'tickerContainer'.", false);
					ErrorCount += 1;
					return;
				}
				htmlText = htmlText.Remove(0, index);
				index = htmlText.IndexOf("tblTicker", 0);
				if (index == -1)
				{
					WriteToLogFile("Invalid html response. No 'tblTicker'.", false);
					ErrorCount += 1;
					return;
				}
				htmlText = htmlText.Remove(0, index);
				//htmlText = DoWebRequest("http://www.naftemporiki.gr/finance/athexRealTimeTicker", "POST");
				string pattern = @"<TD.+target=_blank>(.+)</A></SPAN></TD>";
				IList<string> stocks = new List<string>();
				IList<string> stockValues = new List<string>();
				foreach (Match match in Regex.Matches(htmlText, pattern, RegexOptions.IgnoreCase))
					stocks.Add(match.Groups[1].Value);
				pattern = @"<TD.+qlast.*>([,0-9]+.+)</SPAN></TD>";
				foreach (Match match in Regex.Matches(htmlText, pattern, RegexOptions.IgnoreCase))
					stockValues.Add(Regex.Replace(match.Groups[1].Value, @"</SPAN.+nbsp;", "|"));
				if (stocks.Count() != stockValues.Count())
				{
					WriteToLogFile("Invalid html response.", false);
					ErrorCount += 1;
					return;
				}
				IList<string> checkStocks = MyStocksParams.Select(obj => obj.StockName).ToList();
				for (int i = 0; i <= stocks.Count - 1; i++)
				{
					LatestStocksAndValues.Add(new StockValuePrc(stocks[i], stockValues[i]));
					//HistoryStocksAndValues.Add(new StockValuePrc(stocks[i], stockValues[i]));
				}

				decimal tmp = 0;
				foreach (StockParameters sparam in MyStocksParams)
				{
					sparam.ClearBackgrouns();
					if (sparam.IsSumRow)
					{
						var x = MyStocksParams.Where(e => !e.IsSumRow && e.CurrentProfit.NumberValue.HasValue).Sum(e => e.CurrentProfit.NumberValue.Value);
						sparam.CurrentProfit = new ValueWithBackground(x - AmountGained, true);
						x = MyStocksParams.Where(e => !e.IsSumRow).Sum(e => e.BuyAmount) + AmountLost - AmountGained;
						sparam.BuyAmount = Math.Round(x, 2);
						x = MyStocksParams.Where(e => !e.IsSumRow).Sum(e => e.CurrentAmount);
						sparam.CurrentValue = new ValueWithBackground(Math.Round(x, 2), Colors.Black);
						if (x > 0)
						{
							var y = MyStocksParams.Where(e => !e.IsSumRow).Sum(e => e.BuyAmount);
							sparam.CurrentPrc = new ValueWithBackground(((x / y) - 1) * 100, true);
						}
					}
					else
					{
						StockValuePrc stock = LatestStocksAndValues.FirstOrDefault(obj => obj.StockName == sparam.StockName);
						if (stock != null)
						{
							if (stock.StockPrc != null && decimal.TryParse(stock.StockPrc.Replace("%", ""), out tmp))
								sparam.CurrentPrc = new ValueWithBackground(tmp, true);
							else
							{
								tmp = 0;
								sparam.CurrentPrc = new ValueWithBackground(0, true);
							}
							System.Windows.Media.Color clr;
							if (tmp > 0)
								clr = Colors.Green;
							else if (tmp < 0)
								clr = Colors.Red;
							else
								clr = Colors.Black;
							if (stock.StockValue != null && decimal.TryParse(stock.StockValue, out tmp))
								sparam.CurrentValue = new ValueWithBackground(tmp, clr);
						}
						else
						{
							txtMessage.Text = sparam.StockName + " does not exist!";
						}
					}

					if (sparam.WakeOnValueGreater.Blink || sparam.WakeOnValueLower.Blink
						|| sparam.WakeOnNegChange.Blink || sparam.WakeOnPosChange.Blink)
						WakeUp();
				}
				lblLastUpdate.Text = "Last updated: " + DateTime.Now.ToString("HH:mm:ss");
				SoundPlayer player = new SoundPlayer(Properties.Resources.Normal);
				player.Play();
			}
			catch (Exception ex)
			{
				SoundPlayer player = new SoundPlayer(Properties.Resources.Failure);
				player.Play();
				WriteToLogFile(ex.Message + "\t\tStackTrace: " + ex.StackTrace, false);
			}
			finally
			{
				Mouse.OverrideCursor = Cursors.Arrow;
			}
		}

		private void CheckStockValuesCapt()
		{
			Mouse.OverrideCursor = Cursors.Wait;
			txtMessage.Text = "";
			LatestStocksAndValues = new List<StockValuePrc>();
			try
			{
				StocksForCheck = string.Join("|", MyStocksParams.Where(e => !e.IsSumRow).Select(e => e.ToString()));
				dynamic doc = wb.Document;
				string htmlText = doc.documentElement.InnerHtml;
				int index = htmlText.IndexOf("tickervalues", 0);
				if (index == -1)
				{
					WriteToLogFile("Invalid html response. No 'tickervalues'.", false);
					ErrorCount += 1;
					return;
				}
				htmlText = htmlText.Remove(0, index);
				index = htmlText.IndexOf("</TBODY", 0);
				if (index == -1)
				{
					WriteToLogFile("Invalid html response. No '</TBODY'.", false);
					ErrorCount += 1;
					return;
				}
				htmlText = htmlText.Remove(index);
				index = htmlText.IndexOf("TimeLabel", 0);
				if (index == -1)
				{
					WriteToLogFile("Invalid html response. No 'TimeLabel'.", false);
					ErrorCount += 1;
					return;
				}
				htmlText = htmlText.Remove(0, index);
				htmlText = htmlText.Replace("</TR>\r\n", "</TR>|");
				string[] lines = htmlText.Split('|');
				IList<string> stocks = new List<string>();
				IList<string> stockValues = new List<string>();
				for (int i = 0; i <= lines.Length - 1; i++)
				{
					if (lines[i].IndexOf("_blank") > -1)
					{
						lines[i] = lines[i].Remove(0, lines[i].IndexOf("_blank") + 7);
						lines[i] = lines[i].Replace("</A>", "");
						lines[i] = lines[i].Replace("</TD>", "");
						lines[i] = lines[i].Replace("\r\n", "");
						lines[i] = lines[i].Replace("</TR>", "");
						lines[i] = lines[i].Replace("<TD>", "|");
						string[] s = lines[i].Split('|');
						stocks.Add(s[0]);
						stockValues.Add(s[1] + "|" + s[3] + "|" + s[4]);
					}
				}
				IList<string> checkStocks = MyStocksParams.Select(obj => obj.StockName).ToList();
				for (int i = 0; i <= stocks.Count - 1; i++)
				{
					LatestStocksAndValues.Add(new StockValuePrc(stocks[i], stockValues[i]));
					//HistoryStocksAndValues.Add(new StockValuePrc(stocks[i], stockValues[i]));
				}

				decimal tmp = 0;
				foreach (StockParameters sparam in MyStocksParams)
				{
					sparam.ClearBackgrouns();
					if (sparam.IsSumRow)
					{
						var x = MyStocksParams.Where(e => !e.IsSumRow && e.CurrentProfit.NumberValue.HasValue).Sum(e => e.CurrentProfit.NumberValue.Value);
						sparam.CurrentProfit = new ValueWithBackground(x - AmountGained - AmountLost, true);
						var y = MyStocksParams.Where(e => !e.IsSumRow).Sum(e => e.SellAmount);
						sparam.SellAmount = Math.Round(y, 2);
						y = MyStocksParams.Where(e => !e.IsSumRow).Sum(e => e.BuyAmount) + AmountLost + AmountGained;
						sparam.BuyAmount = Math.Round(y, 2);
						x = MyStocksParams.Where(e => !e.IsSumRow).Sum(e => e.CurrentAmount);
						sparam.CurrentValue = new ValueWithBackground(Math.Round(x, 2), Colors.Black);
						if (x != 0)
							sparam.CurrentPrc = new ValueWithBackground(((x / y) - 1) * 100, true);
						this.Title = "RT stocks [" + sparam.CurrentProfit.NumberValue + "€ (" + sparam.CurrentPrc.NumberValue.Value.ToString("N2") + "%)]";
						ChangeTaskBarIcon(sparam.CurrentPrc.NumberValue.Value);
					}
					else
					{
						var qwe = LatestStocksAndValues.FirstOrDefault(obj => obj.StockName == sparam.StockName);
						if (qwe != null)
						{
							StockValuePrc stock = qwe;
							if (stock.StockPrc != null && decimal.TryParse(stock.StockPrc.Replace("%", ""), out tmp))
								sparam.CurrentPrc = new ValueWithBackground(tmp, true);
							else
							{
								tmp = 0;
								sparam.CurrentPrc = new ValueWithBackground(0, true);
							}
							System.Windows.Media.Color clr;
							if (tmp > 0)
								clr = Colors.Green;
							else if (tmp < 0)
								clr = Colors.Red;
							else
								clr = Colors.Black;
							if (stock.StockValue != null && decimal.TryParse(stock.StockValue, out tmp))
								sparam.CurrentValue = new ValueWithBackground(tmp, clr);
							//if (stock.Volume != null && decimal.TryParse(stock.Volume, out tmp))
							//	sparam.Volume += (int) tmp;
						}
						else
						{
							txtMessage.Text = sparam.StockName + " does not exist!";
						}
					}

					if (sparam.WakeOnValueGreater.Blink || sparam.WakeOnValueLower.Blink
						|| sparam.WakeOnNegChange.Blink || sparam.WakeOnPosChange.Blink)
						WakeUp();
				}
				lblLastUpdate.Text = "Last updated: " + DateTime.Now.ToString("HH:mm:ss");
				SoundPlayer player = new SoundPlayer(Properties.Resources.Normal);
				player.Play();
			}
			catch (Exception ex)
			{
				SoundPlayer player = new SoundPlayer(Properties.Resources.Failure);
				player.Play();
				WriteToLogFile(ex.Message + "\t\tStackTrace: " + ex.StackTrace, true);
			}
			finally
			{
				Mouse.OverrideCursor = Cursors.Arrow;
			}
		}

		private decimal _previousVal = 0;
		private void ChangeTaskBarIcon(decimal val)
		{
			Bitmap bitmap = new Bitmap(40, 40, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
			Graphics g = Graphics.FromImage(bitmap);
			System.Drawing.Brush brs;
			if (Math.Round(_previousVal, 3) == Math.Round(val, 3))
				brs = System.Drawing.Brushes.Gray;
			else
			{
				if (val > 0)
					brs = System.Drawing.Brushes.Green;
				else
					brs = System.Drawing.Brushes.Red;
			}
			string s = NumberToString(val);
			g.DrawString(s, new Font("Calibri", 33 - (4 * s.Length), System.Drawing.FontStyle.Bold),
				brs, new PointF(0, 0));
			IntPtr hBitmap = bitmap.GetHbitmap();
			BitmapSource wpfBitmap = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty,
																			BitmapSizeOptions.FromEmptyOptions());
			TaskbarItemInfo.Overlay = wpfBitmap;
			if (_previousVal <= val)
				TaskbarItemInfo.ProgressState = TaskbarItemProgressState.Normal;
			//else if (_previousVal == val)
			//	TaskbarItemInfo.ProgressState = TaskbarItemProgressState.Indeterminate;
			else
				TaskbarItemInfo.ProgressState = TaskbarItemProgressState.Error;
			_previousVal = val;
		}

		private string NumberToString(decimal val)
		{
			decimal tmp = Math.Abs(val);
			if (tmp >= 10)
				return ((int)tmp).ToString();
			else
				return ((decimal)((int)(tmp * 10)) / 10).ToString();
		}

		public static BitmapSource CreateBitmapSourceFromBitmap(Bitmap bitmap)
		{
			if (bitmap == null)
				throw new ArgumentNullException("bitmap");

			return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
				bitmap.GetHbitmap(),
				IntPtr.Zero,
				Int32Rect.Empty,
				BitmapSizeOptions.FromEmptyOptions());
		}


		private void WakeUp()
		{
			//PreviousWinState = this.WindowState;
			//this.WindowState = (WindowState)(((int)WindowState + 1) % 3);
			//this.WindowState = (PreviousWinState == WindowState.Minimized) ? WindowState.Normal : PreviousWinState;
			WindowState = System.Windows.WindowState.Maximized;
			Topmost = true;
			Topmost = false;
		}

		public string DoWebRequest(string address, string method)
		{
			var request = (HttpWebRequest)WebRequest.Create(address);
			request.Method = method;
			//request.Credentials = new NetworkCredential("demo@leankitkanban.com", "demopassword");
			request.PreAuthenticate = true;
			request.ContentLength = 0;
			request.Timeout = 15000;
			request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
			string output = string.Empty;
			try
			{
				using (var response = request.GetResponse())
				{
					using (var stream = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(1252)))
					{
						output = stream.ReadToEnd();
					}
				}
			}
			catch (WebException ex)
			{
				if (ex.Status == WebExceptionStatus.ProtocolError)
				{
					using (var stream = new StreamReader(ex.Response.GetResponseStream()))
					{
						output = stream.ReadToEnd();
					}
				}
				else if (ex.Status == WebExceptionStatus.Timeout)
				{
					output = "Request timeout is expired.";
				}
			}
			return output;
		}

		private void btnCheck_Click(object sender, RoutedEventArgs e)
		{
			CheckStockValues();
		}

		private void wb_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
		{
			GetValuesTimer.Start();
		}

		private void WriteToLogFile(string msg, bool ShowMessage)
		{
			if (ShowMessage)
				MessageBox.Show(msg);
			string logFileDir = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
			File.AppendAllText(logFileDir + "\\LogFile.txt", System.DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss") + " = " + msg + Environment.NewLine);
		}

		private void chkUpdate_Checked(object sender, RoutedEventArgs e)
		{
			RefreshWBtimer.Start();
			TaskbarItemInfo.ProgressState = TaskbarItemProgressState.Normal;
			ProgressTimer.Start();
		}

		private void chkUpdate_Unchecked(object sender, RoutedEventArgs e)
		{
			RefreshWBtimer.Stop();
			ProgressTimer.Stop();
			TaskbarItemInfo.ProgressValue = 0;
			TaskbarItemInfo.ProgressState = TaskbarItemProgressState.None;
		}

		private void Window_Loaded(object sender, RoutedEventArgs e)
		{
			StocksForCheck = Settings.Default.MyStocks;
			txtAlreadyGained.Text = Settings.Default.AmountGained.ToString();
			txtAlreadyLost.Text = Settings.Default.AmountLost.ToString();
			MyStocksParams = StocksForCheck.Split('|').Select(obj => new StockParameters(obj)).ToList();
		}

		private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
		{
			Settings.Default.MyStocks = StocksForCheck;
			Settings.Default.AmountGained = AmountGained;
			Settings.Default.AmountLost = AmountLost;
			Settings.Default.Save();
		}

		private void UpdateParams()
		{
			MyStocksParams = StocksForCheck.Split('|').Select(obj => new StockParameters(obj)).ToList();
		}

		private void dgMyStocks_LoadingRow(object sender, System.Windows.Controls.DataGridRowEventArgs e)
		{
			StockParameters item = e.Row.Item as StockParameters;
			if (item != null)
			{
				if (item.IsSumRow)
					e.Row.Background = new SolidColorBrush(Colors.LightGray);
			}
		}

		private void btnDelete_Click(object sender, RoutedEventArgs e)
		{
			if (MessageBox.Show("Really delete this?", "Confirm", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
			{
				StockParameters stock = (dgMyStocks.SelectedValue as StockParameters);
				_myStocksParams.Remove(stock);
				CheckStockValues();
				UpdateParams();
			}
		}

		private void btnAdd_Click(object sender, RoutedEventArgs e)
		{
			if (LatestStocksAndValues.Count() < 1)
			{
				MessageBox.Show("Please wait to retrieve the values...");
				return;
			}
			if (LatestStocksAndValues.Where(obj => obj.StockName == txtAddStockName.Text.ToUpper()).Count() == 0)
			{
				MessageBox.Show("Not existing stock name");
				return;
			}
			if (MyStocksParams.Where(obj => obj.StockName == txtAddStockName.Text.ToUpper()).Count() > 0)
			{
				MessageBox.Show("This stock already exists");
				return;
			}
			txtAddStockName.Text = txtAddStockName.Text.ToUpper();
			StockParameters stock = new StockParameters(txtAddStockName.Text);
			_myStocksParams.Add(stock);
			CheckStockValues();
			UpdateParams();
			txtAddStockName.Text = "";
			txtAddStockName.Focus();
		}

		private void txtGetValuesSecs_LostFocus(object sender, RoutedEventArgs e)
		{
			GetValuesTimer.Interval = new TimeSpan(0, 0, GetValuesInterval);
		}

		private void Window_KeyDown(object sender, KeyEventArgs e)
		{
			if (e.Key == Key.Escape)
				MinimizeWindow();
		}

		private void MinimizeWindow()
		{
			PreviousWinState = this.WindowState;
			WindowState = WindowState.Minimized;
		}

		private void txtAddStockName_KeyDown(object sender, KeyEventArgs e)
		{
			if (e.Key == Key.Enter)
				btnAdd_Click(null, null);
		}

		private void btnRefresh_Click(object sender, RoutedEventArgs e)
		{
			GetValues();
		}

		private void btnOpenLog_Click(object sender, RoutedEventArgs e)
		{
			var logPath = Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
			logPath = logPath + "\\LogFile.txt";
			txtMessage.Text = "Opening file: " + logPath;
			if (File.Exists(logPath))
				Process.Start(logPath);
			else
				MessageBox.Show("No logs found", "System message:");
		}

		private void btnOpenExcel_Click(object sender, RoutedEventArgs e)
		{
			var excelPath = Settings.Default.SupportingExcelFile;
			txtMessage.Text = "Opening file: " + excelPath;
			if (File.Exists(excelPath))
				Process.Start(excelPath);
			else
				MessageBox.Show("The excel file: '"+ excelPath + "' does not exist.", "System message:");
		}

		private void btnSetExcelPath_Click(object sender, RoutedEventArgs e)
		{
			OpenFileDialog dlg = new OpenFileDialog();
			dlg.FileName = "";
			dlg.DefaultExt = ".xlsx";
			dlg.Filter = "Excel files (*.xls or *.xlsx)|*.xls;*.xlsx";
			bool? result = dlg.ShowDialog();
			if (result.HasValue && result.Value)
			{
				Settings.Default.SupportingExcelFile = dlg.FileName;
				Settings.Default.Save();
			}
		}
	}
}
